package com.xbongbong.paas.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.gateway.config.GatewayConstant;
import com.xbongbong.paas.gateway.help.HttpHelp;
import com.xbongbong.paas.gateway.pojo.vo.PlayBackVO;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.toolbox.util.DateUtil;
import com.xbongbong.pro.config.ProBaseConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.Objects;

import static com.xbongbong.paas.gateway.filter.AuthenticationFilter.XBB_GATE_CACHE_REQUEST_BODY_JSON_KEY;

/**
 * 流量回放过滤器
 * @author 李少杰
 * @version v1.0.0
 * @date 2020/9/8 16:28
 * @since v1.0.0
 */
@Component
public class FlowPlayBackFilter implements GlobalFilter, Ordered {
    private  static final Logger log =  LoggerFactory.getLogger(FlowPlayBackFilter.class);

    /**
     * 流量回放开启标志
     */
    private static final String PLAY_BACK_SWITCH_ON = "on";

    @Resource
    private HttpHelp httpHelp;

    @Resource
    private PaasRedisHelper paasRedisHelper;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        try {
            String switchStatus = paasRedisHelper.getValue(RedisPrefixConstant.PLAY_BACK_SWITCH, RedisPrefixConstant.PLAY_BACK_SWITCH);
            //开关未开启，放行
            if (Objects.isNull(switchStatus) || (! PLAY_BACK_SWITCH_ON.equals(switchStatus))) {
                return chain.filter(exchange);
            }
            //忽略特定请求的流量录制
            ServerHttpRequest request = exchange.getRequest();
            if (ignoreRequest(request)) {
                return chain.filter(exchange);
            }
            //验证白名单
            JSONObject requestBodyJson = (JSONObject) exchange.getAttributes().get(XBB_GATE_CACHE_REQUEST_BODY_JSON_KEY);
            String corpid = requestBodyJson.getString("corpid");
            if (! paasRedisHelper.isMember(RedisPrefixConstant.PLAY_BACK_WHITE_CORP_SET, RedisPrefixConstant.PLAY_BACK_WHITE_CORP_SET, corpid)) {
                return chain.filter(exchange);
            }
            sendPost(corpid, request, requestBodyJson);
        } catch (Exception e) {
            log.error("FlowPlayBackFilter 流量回放处理发生异常： ", e);
        }
        //放行，不修改请求，交给下一过滤器
        return chain.filter(exchange);
    }

    /**
     * 优先级在权限验证之后，只录制能正常访问系统的流量
     * @return int
     */
    @Override
    public int getOrder() { return Ordered.HIGHEST_PRECEDENCE + 1003; }

    /**
     * 发送录制流量到流量回放服务器
     * @param corpid 公司id
     * @param request request请求
     * @param requestBodyJson 请求体
     */
    private void sendPost(String corpid, ServerHttpRequest request, JSONObject requestBodyJson) {
        //封装流量回放需要的数据
        PlayBackVO playBackVO = new PlayBackVO();
        playBackVO.setUrl(request.getURI().getPath());
        playBackVO.setCorpid(corpid);
        playBackVO.setHeaders(request.getHeaders().toString());
        playBackVO.setBody(requestBodyJson.toJSONString());
        playBackVO.setHttpMethod(Objects.requireNonNull(request.getMethod()).name());
        playBackVO.setAddTime( DateUtil.getLong());
        httpHelp.sendPost4RestTemplate(ProBaseConfig.getXbbPlayBackUrl(), playBackVO);
    }

    /**
     * 忽略特定请求的流量
     * @param request request
     * @return true：跳过录制
     */
    private boolean ignoreRequest(ServerHttpRequest request){
        String path = request.getURI().getPath();
        //允许跳过的请求
        for (String noFilterPrefix : GatewayConstant.NO_FILTER_PATH_PREFIX) {
            if(path.startsWith(noFilterPrefix)) {
                return true;
            }
        }
        return false;
    }
}
